#include "BTreeNode.h"

#include "stdio.h"
#include <cstring>
#include <stdlib.h>

using namespace std;


/**
 * Constructor for BTLeafNode, set buffer to zero array.
 */
BTLeafNode::BTLeafNode()
{

	int nodeLength = Node<RecordId>::getNodeSize();
	int capacity = (int)(				     //Cast the final result to an integer representing size in bytes.
						(PageFile::PAGE_SIZE //From total size
						 - sizeof(int)       //Subtract length of the size attribute in the DLL
						 - sizeof(PageId)    //Subtract the length of the page ID field that points to the next node.
						 )                   //Gives us the space left to store the individual nodes.
						 / nodeLength);		 //Divide the total space by the size of individual nodes.


	list = new DoublyLinkedList<RecordId>();

	//TODO: Remove before production.
	capacity = 2;

 	list->setCapacity(capacity);
}

BTLeafNode::~BTLeafNode(){
	if(this->list){
		//delete this->list;
	}
}

void BTLeafNode::print(){
	list->print();
}

void BTLeafNode::setList(DoublyLinkedList<RecordId>* list){

	if(this->list){
		delete this->list;
	}

	this->list = list;
}

/*
 * Read the content of the node from the page pid in the PageFile pf.
 * @param pid[IN] the PageId to read
 * @param pf[IN] PageFile to read from
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::read(PageId pid, const PageFile& pf)
{
	//Wipe the buffer.
	memset(buffer, 0, PageFile::PAGE_SIZE);

	//Read the page into the buffer. Remember that a page file has multiple pages.
	RC result = pf.read(pid, buffer) ;

	if(result != 0)
		return result;

	int offset = 0;

	//Read the ID of the next page from the buffer.
	memcpy(&nextNodeId, &buffer[offset], sizeof(PageId));
	offset += sizeof(PageId);

	list->empty();
	list->read(buffer, offset);

	return 0;
}
    
/*
 * Write the content of the node to the page pid in the PageFile pf.
 * @param pid[IN] the PageId to write to
 * @param pf[IN] PageFile to write to
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::write(PageId pid, PageFile& pf)
{
	//Wipe the buffer.
	memset(buffer, 0, PageFile::PAGE_SIZE);

	int offset = 0;

	//Write the ID of the next page into the buffer.
	memcpy(&buffer[offset], &nextNodeId, sizeof(PageId));
	offset += sizeof(PageId);

	//Write the contents of the doubly linked list to the buffer.
	list->write(buffer, offset);

	//Write the buffer into the page. Remember that a page file has multiple pages.
	return pf.write(pid, buffer);
}

/*
 * Return the number of keys stored in the node.
 * @return the number of keys in the node
 */
int BTLeafNode::getKeyCount()
{
	return list->getSize();
}

/*
 * Insert a (key, rid) pair to the node.
 * @param key[IN] the key to insert
 * @param rid[IN] the RecordId to insert
 * @return 0 if successful. Return an error code if the node is full.
 */
RC BTLeafNode::insert(int key, const RecordId& rid)
{
	if(list->isFull()){
		return RC_NODE_FULL;
	}else{
		return list->insertSorted(key, rid);
	}
}

/*
 * Insert the (key, rid) pair to the node and split the node half and half with sibling.
 * The first key of the sibling node is returned in siblingKey.
 * @param key[IN] the key to insert.
 * @param rid[IN] the RecordId to insert.
 * @param sibling[IN] the sibling node to split with. This node MUST be EMPTY when this function is called.
 * @param siblingKey[OUT] the first key in the sibling node after split.
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::insertAndSplit(int key, const RecordId& rid, BTLeafNode& sibling, int& siblingKey)
{

	//Force insert to ignore the capacity constraint.
	list->insertSorted(key, rid, true);

	//Split the list in half.
	DoublyLinkedList<RecordId>* half = list->split((int)(list->getSize() / 2.0 + 0.5));

	sibling.setList(half);

	siblingKey = half->getHead()->getKey();

	return 0;
}

/*
 * Find the entry whose key value is larger than or equal to searchKey
 * and output the eid (entry number) whose key value >= searchKey.
 * Remember that all keys inside a B+tree node should be kept sorted.
 * @param searchKey[IN] the key to search for
 * @param eid[OUT] the entry number that contains a key larger than or equal to searchKey
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::locate(int searchKey, int& key, RecordId& rid)
{
	Node<RecordId>* currentNode = list->getHead();

	while(currentNode != 0){

		if(searchKey <= currentNode->getKey()){

			key = currentNode->getKey();
			rid = currentNode->getValue();

			return 0;
		}

		currentNode = currentNode->getNext();
	}

	return 0;


}


/*
 * Return the pid of the next sibling node.
 * @return the PageId of the next sibling node 
 */
PageId BTLeafNode::getNextNodePtr()
{
	return nextNodeId;
}

/*
 * Set the pid of the next slibling node.
 * @param pid[IN] the PageId of the next sibling node 
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::setNextNodePtr(PageId pid)
{
	nextNodeId = pid;
	return 0;
}












/*#################  BTNonLeafNode Implementation  #################*/

/**
 * Constructor for BTNonLeafNode, set buffer to zero array.
 */
BTNonLeafNode::BTNonLeafNode()
{

	int nodeLength = Node<PageId>::getNodeSize();
	int capacity = (int)(				     //Cast the final result to an integer representing size in bytes.
						(PageFile::PAGE_SIZE //From total size
						 - sizeof(int)       //Subtract length of the size attribute in the DLL
						 - sizeof(PageId)    //Subtract the length of the page ID field that points to the next node.
						 )                   //Gives us the space left to store the individual nodes.
						 / nodeLength);		 //Divide the total space by the size of individual nodes.

	list = new DoublyLinkedList<PageId>();

	//TODO: Remove before production.
	capacity = 2;
 	list->setCapacity(capacity);
}

BTNonLeafNode::~BTNonLeafNode(){
	if(this->list){
		//delete this->list;
	}
}

void BTNonLeafNode::print(){
	list->print();
}

void BTNonLeafNode::setList(DoublyLinkedList<PageId>* list){

	if(this->list){
		delete this->list;
	}

	this->list = list;
}

/*
 * Read the content of the node from the page pid in the PageFile pf.
 * @param pid[IN] the PageId to read
 * @param pf[IN] PageFile to read from
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::read(PageId pid, const PageFile& pf)
{
	//Wipe the buffer.
	memset(buffer, 0, PageFile::PAGE_SIZE);

	//Read the page into the buffer. Remember that a page file has multiple pages.
	RC result = pf.read(pid, buffer) ;

	if(result != 0)
		return result;

	int offset = 0;

	//Read the ID of the next page from the buffer.
	memcpy(&nextNodeId, &buffer[offset], sizeof(PageId));
	offset += sizeof(PageId);

	list->empty();
	list->read(buffer, offset);

	return 0;
}
    
/*
 * Write the content of the node to the page pid in the PageFile pf.
 * @param pid[IN] the PageId to write to
 * @param pf[IN] PageFile to write to
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::write(PageId pid, PageFile& pf)
{
	//Wipe the buffer.
	memset(buffer, 0, PageFile::PAGE_SIZE);

	int offset = 0;

	//Write the ID of the next page into the buffer.
	memcpy(&buffer[offset], &nextNodeId, sizeof(PageId));
	offset += sizeof(PageId);

	//Write the contents of the doubly linked list to the buffer.
	list->write(buffer, offset);

	//Write the buffer into the page. Remember that a page file has multiple pages.
	return pf.write(pid, buffer);}

/*
 * Return the number of keys stored in the node.
 * @return the number of keys in the node
 */
int BTNonLeafNode::getKeyCount()
{
	return list->getSize();
}


/*
 * Insert a (key, pid) pair to the node.
 * @param key[IN] the key to insert
 * @param pid[IN] the PageId to insert
 * @return 0 if successful. Return an error code if the node is full.
 */
RC BTNonLeafNode::insert(int key, PageId pid)
{
	if(list->isFull()){
		return RC_NODE_FULL;
	}else{
		return list->insertSorted(key, pid);
	}
}

/*
 * Insert the (key, pid) pair to the node
 * and split the node half and half with sibling.
 * The middle key after the split is returned in midKey.
 * @param key[IN] the key to insert
 * @param pid[IN] the PageId to insert
 * @param sibling[IN] the sibling node to split with. This node MUST be empty when this function is called.
 * @param midKey[OUT] the key in the middle after the split. This key should be inserted to the parent node.
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::insertAndSplit(int key, PageId pid, BTNonLeafNode& sibling, int& midKey)
{
	//Force insert to ignore the capacity constraint.
	list->insertSorted(key, pid, true);

	//Split the list in half.
	DoublyLinkedList<PageId>* half = list->split((int)(list->getSize() / 2.0 + 0.5));

	sibling.setList(half);

	midKey = half->getHead()->getKey();

	return 0;
}

/*
 * Given the searchKey, find the child-node pointer to follow and
 * output it in pid.
 * @param searchKey[IN] the searchKey that is being looked up.
 * @param pid[OUT] the pointer to the child node to follow.
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::locateChildPtr(int searchKey, PageId& pid)
{
	Node<PageId>* node = list->findKey(searchKey);

	if(node == 0){
		//ToDo: return the correct error.
	}else{
		pid = node->getValue();
	}

	return 0;
}

/*
 * Initialize the root node with (pid1, key, pid2).
 * @param pid1[IN] the first PageId to insert
 * @param key[IN] the key that should be inserted between the two PageIds
 * @param pid2[IN] the PageId to insert behind the key
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::initializeRoot(PageId pid1, int key, PageId pid2)
{
	list->empty();

	list->insert(key, pid1);

	nextNodeId = pid2;

	return true;
}
